#!/usr/bin/env python
#Copyright (c) 2011 Isaac Dawson (WBTS Project)
#Permission is hereby granted, free of charge, to any person obtaining a copy 
#of this software and associated documentation files (the "Software"), to deal 
#in the Software without restriction, including without limitation the rights 
#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
#copies of the Software, and to permit persons to whom the Software is furnished
#to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all
#copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
#THE SOFTWARE.

from twisted.web import server
from twisted.python import components, log
from xml.etree import ElementTree
from binascii import a2b_base64
import cgi

import glob
import os
import xml_utils

################################################
###   Some basic response helper functions   ###
################################################
def json_internal_error(request, message, deferred=True):
    request.setHeader("content-type", "application/json")
    request.write("{msg: '%s'}"%message)
    if (deferred == False):
        return server.NOT_DONE_YET
    else:
        request.finish()
    
def not_found(request, errmsg="No authority Found"):
    request.setResponseCode(404, errmsg)
    request.finish()
    return server.NOT_DONE_YET

def json_err_response(request, errmsg):
    request.write('{"msg": "%s"}'%errmsg)
    request.finish()
    return

def json_ok_response(request, msg="OK"):
    request.write('{"msg": "%s"}'%msg)
    request.finish()
    return server.NOT_DONE_YET

def check_required(request, value):
    if (value not in request.args.keys() or request.args[value][0] == ""):
        return json_err_response(request, "%s not found or empty"%value)
    return 0

def not_empty(rargs, value):
    if (value in rargs.keys() and rargs[value][0] != ""):
        return True
    return False

# If the client does not send a content-type application/x-www-form-urlencoded
# the request.args will NOT get populated, this func will populate it ;>
def custom_arg_parser(request):
    request.args.update(cgi.parse_qs(request.content.read(), 1))
    return request


def parse_headers(headers_str):
    parsed = []
    headers = headers_str.split("\r\n")
    # remove any extraneous \r\n's that got split into an empty set.
    try:
        headers.remove('')
    except ValueError:
        pass
    for header in headers:
        try:
            name,value = header.split(':', 1) # only split on the first :
            if (value.startswith(' ') and len(value) > 1):
                value = value[1:]
            parsed.append([name,value])
        except ValueError, msg:
            log.err("ERROR We received an invalid header/value pair: %s"%msg)
    return parsed

    
################################################
### End Some basic response helper functions ###
################################################

def full_escape(data):
    filtered = cgi.escape(data)
    filtered = filtered.replace("\\","\\\\")
    filtered = filtered.replace('\'',"\\\'")
    return filtered

def clean_result(result, request, needs_decode=None):
    if (needs_decode == None):
        return
    
    result["_id"] = str(result["_id"])
    if (needs_decode == None):
        return
    
    for decode in needs_decode:
        try:
            result[decode] = a2b_base64(result[decode])
            result[decode] = unicode(result[decode], errors='ignore')
        except KeyError, msg:
            log.err("ERROR %s not found! Unable to decode."%decode)
    return
    
################################################
###  Some basic testcase / vhost functions   ###
################################################
        
def find_client_id(url):
    client_id = ''
    if ("?client_id=" in url):
        client_id = url.split('?client_id=')[1]
    return client_id


def remove_temp_record(record, resource, pre_db, needs_cb, request=None):
    if (record == {} or record == None):
        # temp record is empty
        return server.NOT_DONE_YET
    d = pre_db.remove(record, safe=True)
    if (request != None): #a normal resource
        if (needs_cb == True):
            d.addCallback(resource._cb_Ok, request)
        d.addErrback(resource._cb_Err, request)
        return server.NOT_DONE_YET
    if (needs_cb == True):
        d.addCallback(resource._cb_Ok)
    d.addErrback(resource._cb_Err)
    return d
    

def fail_test(record, resource, pre_db, db, request=None ):
    """
    Takes a record (probably from a defered) then inserts it into the real DB
    the pretest result is pre-set to failed/result = TEST FAILED so we can just
    copy as is into the real results db. Provided there is a record,
    the result of the insert is then sent to the remove_temp_record method to
    delete the pretest result info. That function then calls the final callback
    function stating that the record has been copied & deleted successfully.
    (OR, an error occurred in which case the cb_err is called.)
    This function is kind of ugly because we need to return either defereds for
    normal resources, or return codes for xmlrpc resources. That is why we use a
    default parameter for the request and set it to None. None signifies that
    the type is xmlrpc.
    """
    if (record == {} or record == None):
        # No record == -1
        if (request == None):
            return -1
        else:
            resource._cb_Err("Could not find record in our pretest DB!",
                             request)
            return server.NOT_DONE_YET
    # insert the record from our pretest db into the results db.
    d = db.insert(record, safe=True)
    # set it up to be removed from the pretest db (ignore the return code)
    d.addCallback(lambda ret: remove_temp_record(record,
                                                 resource,
                                                 pre_db,
                                                 True))
    d.addErrback(resource._cb_Err)
    #xmlrpc takes a defered, where as normal resources take server.NOT_DONE_YET
    if (request == None):
        return d
    return server.NOT_DONE_YET
    